package fons.model.ontology.language;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import com.google.common.collect.Lists;

import fons.util.LiteralExtractor;
import fons.util.SuffixTree;

public class SuffixTreeWordFinder implements WordFinder {
	private final SentenceElementMixinFactory factory = new SentenceElementMixinFactory();

	@Override
	public List<SentenceElement> getElements(Sentence sentence,
			Vocabulary vocabulary) {
		final SuffixTree tree = new SuffixTree(sentence.getText().toLowerCase()); 
		final List<SentenceElement> tmp = Lists.newLinkedList();

		for (String word: vocabulary.getWords()) {
			List<SuffixTree.Entry> entries = tree.findLongestMatchesOf(word, 1, 5);
			if (entries.size() > 0) {
			}
			if (!entries.isEmpty()) {
				Collection<Named> references = vocabulary.getMeanings(word);
				for (SuffixTree.Entry entry: entries) {
					for (Named named: references) {						
						tmp.add(factory.decorate(new SuffixTreeSentenceElement(named, entry)));
					}
				}
			}
		}
		
		final LiteralExtractor literals = new LiteralExtractor(sentence.getText());
		for (LiteralExtractor.Literal literal: literals.getNumbers()) {
			tmp.add(factory.decorate(new SuffixTreeSentenceElement(new Literal(literal.number), tree.new Entry(literal.start, 
					literal.end, 1))));
		}
		
		Collections.sort(tmp);
		return reduceElements(tmp);
	}

	private List<SentenceElement> reduceElements (List<SentenceElement> list) {
		List<SentenceElement> tmp = Lists.newLinkedList();

		SentenceElement next = null;
		tmp.add(list.get(0));
		
		for (int i = 1; i < list.size(); ++i) {
			SentenceElement last = tmp.get(tmp.size()-1);
			next = list.get(i);
			
			if (next.getPosition() == last.getPosition()) {
				if (next.getLength() > last.getLength()) {
					tmp.remove(last);
					tmp.add(next);
				}
			} else if (next.getPosition() > last.getPosition() + last.getLength()) {
				tmp.add(next);
			}
		}

		List<SentenceElement> result = Lists.newLinkedList(tmp);
		//reinclude exact synonymes
		for (SentenceElement element: list) {
			for (SentenceElement resultElement: tmp) {
				if (element.holds(Named.class) && resultElement.holds(Named.class) 
						&& element.get(Named.class) != resultElement.get(Named.class)
						&& element.get(Named.class).getName().equals(resultElement.get(Named.class).getName()))
				{
					result.add(element);
					
				} else if (!element.holds(Named.class) && !result.contains(element)) {
					
					result.add(element);
				}
			}
		}

		Collections.sort(result);
		return result;
	}

	private static class SuffixTreeSentenceElement extends AbstractSentenceElement {
		private final SuffixTree.Entry entry;

		public <T> SuffixTreeSentenceElement (T reference, SuffixTree.Entry entry) {
			super(reference);
			this.entry = entry;
		}

		@Override
		public String getText() {
			return entry.getEntry();
		}

		@Override
		public int getPosition() {
			return entry.getPosition();
		}

		@Override
		public int getLength() {
			return entry.getLength();
		}		
	}

	@Override
	public void addSentenceElementMixin(Class<?> reference,
			Class<? extends SentenceElementMixin> sentenceElement) {
		factory.addSentenceElementMixin(reference, sentenceElement);
	}
}
